package spring.mvc.controller;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@Controller
public class My_08_FileController {
    @GetMapping("/download1")
    public ResponseEntity<StreamingResponseBody> download1(HttpServletResponse response) {
        // String fileName = "xxx";
        // String filePath = "D:/123.txt";
        // // 设置响应头和客户端保存文件名
        // response.setCharacterEncoding("utf-8");
        // response.setContentType("multipart/form-data");
        // response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
        // // 打开本地文件流
        // InputStream inputStream = new FileInputStream(filePath);
        // // 激活下载操作
        // OutputStream os = response.getOutputStream();
        // // 循环写入输出流
        // byte[] b = new byte[2048];
        // int length;
        // int downloadedLength = 0;
        // while ((length = inputStream.read(b)) > 0) {
        //     os.write(b, 0, length);
        //     downloadedLength += b.length;
        // }
        // // 这里主要关闭
        // os.close();
        // inputStream.close();

        StreamingResponseBody responseBody = outputStream -> {
            try (ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {
                oos.writeObject("abc".getBytes(StandardCharsets.UTF_8));
            }
        };

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.json")
                .contentType(MediaType.APPLICATION_OCTET_STREAM).body(responseBody);
    }


    // 处理下载 get/post/put 请求等等都是可以的，但一般都用 get 请求
    @RequestMapping(value = "/download2", method = RequestMethod.GET)
    public ResponseEntity<Resource> download2(@RequestParam String fileName) {
        // 构造下载对象，读取出一个 Resource 出来，此处以类路径下的 logback.xml
        DownloadFileInfoDto downloadFile = new DownloadFileInfoDto(fileName, new ClassPathResource("logback.xml"));
        return downloadResponse(downloadFile);
    }

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    private static class DownloadFileInfoDto {
        private String fileName; // 下载的文件
        private Resource resource; // 下载的具体文件资源
    }

    // 共用方法，兼容到了 IE 浏览器
    private static ResponseEntity<Resource> downloadResponse(DownloadFileInfoDto fileInfo) {
        String fileName = fileInfo.getFileName();
        Resource body = fileInfo.getResource();

        // 通过 User-Agent 来判断浏览器类型，做一定的兼容
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String header = request.getHeader("User-Agent").toUpperCase();
        HttpStatus status = HttpStatus.CREATED;
        try {
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "UTF-8");
                fileName = fileName.replace("+", "%20"); // IE 下载文件名空格变+号问题
                status = HttpStatus.OK;
            } else { // 其它浏览器，比如谷歌浏览器等等
                fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
            }
        } catch (UnsupportedEncodingException e) {
        }

        // 响应头需设置为 MediaType.APPLICATION_OCTET_STREAM
        HttpHeaders headers = new HttpHeaders();
        // 注意：这个响应头不是必须的，但是如果你确定要下载，建议写上这个响应头
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        // 此处，如果你自己没有设置这个请求头，浏览器肯定就不会弹窗对话框了。它就会以 body 的形式直接显示在浏览器上
        headers.setContentDispositionFormData("attachment", fileName);
        return new ResponseEntity<>(body, headers, status);
    }

    @RequestMapping("/download3")
    public ResponseEntity<byte[]> download3(HttpSession session) throws IOException {
        byte[] body = null;
        ServletContext servletContext = session.getServletContext();
        InputStream in = servletContext.getResourceAsStream("/files/abc.txt");
        body = new byte[in.available()];
        in.read(body);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", "attachment;filename=abc.txt");

        HttpStatus statusCode = HttpStatus.OK;

        ResponseEntity<byte[]> response = new ResponseEntity<>(body, headers, statusCode);
        return response;
    }

    // ==============================================================================================================

    // 模拟使用 Resource 进行文件的上传
    @ResponseBody
    @RequestMapping(value = "/upload1", method = RequestMethod.POST)
    public String upload1(@RequestBody Resource resource) { // 此处不能用接口 Resource resource
        dumpStream(resource);
        return "success";
    }

    // 当属性被标注为 @RequestPart 的话，就会经过 HttpMessageConverter 结合 Content-Type 来解析
    @ResponseBody
    @RequestMapping(value = "/upload2", method = RequestMethod.POST)
    public String upload2(@RequestPart File file, MultipartFile multipartFile, Part part) {
        System.out.println(file);
        System.out.println(multipartFile);
        System.out.println(part);
        return "success";
    }

    // 模拟写文件的操作（此处写到控制台）
    private static void dumpStream(Resource resource) {
        // 1.获取文件资源，会自动关闭流
        try (InputStream is = resource.getInputStream()) {
            // 2.读取资源
            byte[] descBytes = new byte[is.available()];
            is.read(descBytes);
            System.out.println(new String(descBytes, StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
